/******************************************************************************

	COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2010.

	This file is part of the Maxsi Library.

	Maxsi Library is free software: you can redistribute it and/or modify it
	under the terms of the GNU Lesser General Public License as published by
	the Free Software Foundation, either version 3 of the License, or (at your
	option) any later version.

	Maxsi Library is distributed in the hope that it will be useful, but
	WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
	or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
	License for more details.

	You should have received a copy of the GNU Lesser General Public License
	along with Maxsi Library. If not, see <http://www.gnu.org/licenses/>.

	Maxsi Library
	A powerful Cross-Platform C++ General Purpose Library that allows you to
	efficiently create high-performance and reliable applications.

	MaxsiFileSystem.h
	Useful functions for filesystem interaction.

******************************************************************************/

#ifndef MaxsiFileSystem_H
#define MaxsiFileSystem_H

//=============================================================================
//	Redirect functions depending on what string system we made.
//	TODO: Does the existence of the MaxsiFileSystem make these deprecated?
//=============================================================================
#ifdef Maxsi_Using_Unicode_Internally

	#define CreateFileM CreateFileW
	#define CreateFolderActual _wmkdir

#else

	#define CreateFileM CreateFileA
	#define CreateFolderActual _mkdir

#endif

BeginMaxsiNamespace

//=============================================================================
//	Forward declarations.
//=============================================================================
class IFileSystem;
#ifdef Maxsi_Support_W32
class MaxsiFileSystemWindows;
#endif
#ifdef Maxsi_Support_POSIX
class MaxsiFileSystemPOSIX;
#endif
struct MaxsiDirectoryEntry;

//=============================================================================
//	Maxsi File Handling Definitions
//=============================================================================
#define MAXSI_FILE_READ (1 << 0)
#define MAXSI_FILE_WRITE (1 << 1)
#define MAXSI_FILE_READWRITE (MAXSI_FILE_READ & MAXSI_FILE_WRITE)
#define MAXSI_FILE_CREATE (1 << 2)
#define MAXSI_FILE_TRUNCATE (1 << 3)
#define MAXSI_FILE_SEQUENTIAL (1 << 4)


#define MAXSI_DIR_ENTRY_UNKNOWN 0
#define MAXSI_DIR_ENTRY_FILE 1
#define MAXSI_DIR_ENTRY_DIR 2
#define MAXSI_DIR_ENTRY_OTHER 3

struct MaxsiDirectoryEntry
{
	unsigned int Type;
	void* InternalP1;
	void* InternalP2;
	MESTR* FileName;
};

//=============================================================================
//	Define where the MaxsiFileSystem interface is declared.
//=============================================================================
#undef MAXSI_INTERFACE
#define MAXSI_INTERFACE MAXSI_INTERFACE_ABSTRACT_BASECLASS

//=============================================================================
//	IFileSystem - An interface to any given kind of filesystems that can be
//	accessed in a fairly traditonal manner.
//=============================================================================
MAXSI_CLASSLINK IFileSystem : public MaxsiInterface
{
	MAXSI_DECLARE_INTERFACE(IFileSystem);

public:
	MaxsiError ErrorCode;

public:
	//=============================================================================
	//	Copy-pasteable interface starts here:
	//=============================================================================
	virtual MaxsiError GetErrorCode() MAXSI_INTERFACE;
	virtual MaxsiError SetErrorCode(MaxsiError NewErrorCode) MAXSI_INTERFACE;
	virtual MaxsiHandle OpenFile(MESTR* FileName, FLAGS32 Mode) MAXSI_INTERFACE;
	virtual size_t ReadFile(MaxsiHandle FileHandle, BYTE* Buffer, size_t Size) MAXSI_INTERFACE;
	virtual size_t WriteFile(MaxsiHandle FileHandle, BYTE* Buffer, size_t Size) MAXSI_INTERFACE;
	virtual bool SeekFile(MaxsiHandle FileHandle, FileSize_t Offset) MAXSI_INTERFACE;
	virtual bool SetFileSize(MaxsiHandle FileHandle, FileSize_t FileSize) MAXSI_INTERFACE;
	virtual FileSize_t GetFileSize(MaxsiHandle FileHandle) MAXSI_INTERFACE;
	virtual bool CloseFile(MaxsiHandle FileHandle) MAXSI_INTERFACE;
	virtual bool CreateFolder(MESTR* FileName) MAXSI_INTERFACE;
	virtual bool CompareFileNames(MESTR* FileName1, MESTR* FileName2) MAXSI_INTERFACE;
	virtual MaxsiError OpenFolder(MESTR* FolderName, MaxsiDirectoryEntry* Entry) MAXSI_INTERFACE;
	virtual MaxsiError ReadFolder(MaxsiDirectoryEntry* Entry) MAXSI_INTERFACE;
	virtual void CloseFolder(MaxsiDirectoryEntry* Entry) MAXSI_INTERFACE;

};

#ifdef WIN32
//=============================================================================
//	Define where the MaxsiFileSystemWindows interface is declared.
//=============================================================================
#undef MAXSI_INTERFACE
#define MAXSI_INTERFACE MAXSI_INTERFACE_IMPLEMENTED_IN_MAXSI_LIBRARY

//=============================================================================
//	MaxsiFileSystemWindows - Implements IFileSystem for Microsoft Windows.
//=============================================================================
MAXSI_CLASSLINK MaxsiFileSystemWindows : public IFileSystem
{
	MAXSI_DECLARE_IMPLEMENTATION(MaxsiFileSystemWindows, IFileSystem, MAXSI_INTERFACE_STATIC);

public:
	virtual MaxsiError GetErrorCode() MAXSI_INTERFACE;
	virtual MaxsiError SetErrorCode(MaxsiError NewErrorCode) MAXSI_INTERFACE;
	virtual MaxsiHandle OpenFile(MESTR* FileName, FLAGS32 Mode) MAXSI_INTERFACE;
	virtual size_t ReadFile(MaxsiHandle FileHandle, BYTE* Buffer, size_t Size) MAXSI_INTERFACE;
	virtual size_t WriteFile(MaxsiHandle FileHandle, BYTE* Buffer, size_t Size) MAXSI_INTERFACE;
	virtual bool SeekFile(MaxsiHandle FileHandle, FileSize_t Offset) MAXSI_INTERFACE;
	virtual bool SetFileSize(MaxsiHandle FileHandle, FileSize_t FileSize) MAXSI_INTERFACE;
	virtual FileSize_t GetFileSize(MaxsiHandle FileHandle) MAXSI_INTERFACE;
	virtual bool CloseFile(MaxsiHandle FileHandle) MAXSI_INTERFACE;
	virtual bool CreateFolder(MESTR* FileName) MAXSI_INTERFACE;
	virtual bool CompareFileNames(MESTR* FileName1, MESTR* FileName2) MAXSI_INTERFACE;
	virtual MaxsiError OpenFolder(MESTR* FolderName, MaxsiDirectoryEntry* Entry) MAXSI_INTERFACE;
	virtual MaxsiError ReadFolder(MaxsiDirectoryEntry* Entry) MAXSI_INTERFACE;
	virtual void CloseFolder(MaxsiDirectoryEntry* Entry) MAXSI_INTERFACE;

};

#endif

#ifdef Maxsi_Support_POSIX
//=============================================================================
//	Define where the MaxsiFileSystemPOSIX interface is declared.
//=============================================================================
#undef MAXSI_INTERFACE
#define MAXSI_INTERFACE MAXSI_INTERFACE_IMPLEMENTED_IN_MAXSI_LIBRARY

//=============================================================================
//	MaxsiFileSystemPOSIX - Implements IFileSystem for POSIX.
//=============================================================================
MAXSI_CLASSLINK MaxsiFileSystemPOSIX : public IFileSystem
{
	MAXSI_DECLARE_IMPLEMENTATION(MaxsiFileSystemPOSIX, IFileSystem, MAXSI_INTERFACE_STATIC);

public:
	virtual MaxsiError GetErrorCode() MAXSI_INTERFACE; // WARNING: NOT THREAD SAFE!
	virtual MaxsiError SetErrorCode(MaxsiError NewErrorCode) MAXSI_INTERFACE; // WARNING: NOT THREAD SAFE!
	virtual MaxsiHandle OpenFile(MESTR* FileName, FLAGS32 Mode) MAXSI_INTERFACE;
	virtual size_t ReadFile(MaxsiHandle FileHandle, BYTE* Buffer, size_t Size) MAXSI_INTERFACE;
	virtual size_t WriteFile(MaxsiHandle FileHandle, BYTE* Buffer, size_t Size) MAXSI_INTERFACE;
	virtual bool SeekFile(MaxsiHandle FileHandle, FileSize_t Offset) MAXSI_INTERFACE;
	virtual bool SetFileSize(MaxsiHandle FileHandle, FileSize_t FileSize) MAXSI_INTERFACE;
	virtual FileSize_t GetFileSize(MaxsiHandle FileHandle) MAXSI_INTERFACE;
	virtual bool CloseFile(MaxsiHandle FileHandle) MAXSI_INTERFACE;
	virtual bool CreateFolder(MESTR* FileName) MAXSI_INTERFACE;
	virtual bool CompareFileNames(MESTR* FileName1, MESTR* FileName2) MAXSI_INTERFACE;
	virtual MaxsiError OpenFolder(MESTR* FolderName, MaxsiDirectoryEntry* Entry) MAXSI_INTERFACE;
	virtual MaxsiError ReadFolder(MaxsiDirectoryEntry* Entry) MAXSI_INTERFACE;
	virtual void CloseFolder(MaxsiDirectoryEntry* Entry) MAXSI_INTERFACE;

};

#endif

//=============================================================================
//	Remember to reset MAXSI_INTERFACE when done with it for safety reasons.
//=============================================================================
#undef MAXSI_INTERFACE
#define MAXSI_INTERFACE MAXSI_INTERFACE_RESET_IT_TO_THIS

//=============================================================================
//	MaxsiFileSystem* FileSystem();
//
//	Returns a pointer to the local filesystem class in use on this kernel.
//=============================================================================
MAXSI_LINK IFileSystem* FileSystem();

//=============================================================================
//	MaxsiFileSystem* AppFiles();
//
//	Returns a pointer to a virtual filesystem that makes it easy to access data
//	used by this program near its working dir. It works as a layer upon the
//	local filesystem.
//=============================================================================
MAXSI_LINK IFileSystem* AppFiles();

//=============================================================================
//	MaxsiFileSystem* TempFiles();
//
//	Returns a pointer to a virtual filesystem that makes it easy to store
//	temporary data. It stores stuff in buffers in the local memory until the
//	temporary storage gets too big, then it silently saves stuff to the
//	harddisk so that you can do all sorts of cool stuff.
//=============================================================================
MAXSI_LINK IFileSystem* TempFiles();

//=============================================================================
//	MaxsiHandle MaxsiOpenFile(MESTR* FileName, FLAGS32 Mode);
//
//	Opens a file with the given flags, which are an or'd combinations of the
//	MAXSI_FILE* flags. Returns an open handle to the file on success. The
//	returned handle must be closed with MaxsiCloseHandle() when you are done.
//=============================================================================
MAXSI_LINK MaxsiHandle MaxsiOpenFile(MESTR* FileName, FLAGS32 Mode);

//=============================================================================
//	size_t MaxsiReadFile(MaxsiHandle FileHandle, BYTE* Buffer, size_t Size);
//
//	Reads Size bytes from the file FileHandle into Buffer, if possible,
//	otherwise it reads as much as it can and returns the amount of bytes read
//	into Buffer starting at offset 0. Returns 0 on failure or invalid input.
//=============================================================================
MAXSI_LINK size_t MaxsiReadFile(MaxsiHandle FileHandle, BYTE* Buffer, size_t Size);

//=============================================================================
//	size_t MaxsiWriteFile(MaxsiHandle FileHandle, BYTE* Buffer, size_t Size);
//
//	Writes Size bytes from Buffer to the file FileHandle, if possible. Returns
//	0 on failure. On success, it returns the amount of bytes that were actually
//	written, which can be lower than requested, so you better check whether we
//	succeded or not. Returns 0 or crashes if the input was invalid.
//=============================================================================
MAXSI_LINK size_t MaxsiWriteFile(MaxsiHandle FileHandle, BYTE* Buffer, size_t Size);

//=============================================================================
//	bool MaxsiSeekFile(MaxsiHandle FileHandle, FileSize_t Offset);
//
//	Sets the current position within the file FileHandle in terms of bytes from
//	the start of the file. Returns true on success. If you seek beyond the end
//	of the file, then the bytes between are undefined. Or something like that.
//=============================================================================
MAXSI_LINK bool MaxsiSeekFile(MaxsiHandle FileHandle, FileSize_t Offset);

//=============================================================================
//	bool MaxsiSetFileSize(MaxsiHandle FileHandle);
//
//	Sets the file size of the file. If the filesize is lower than the current
//	file size then data is lost. If it is longer, then the new data is
//	undefined and you should initialize it using MaxsiWriteFile().
//=============================================================================
MAXSI_LINK bool MaxsiSetFileSize(MaxsiHandle FileHandle, FileSize_t Size);

//=============================================================================
//	FileSize_t MaxsiGetFileSize(MaxsiHandle FileHandle);
//
//	Returns the file size the file.
//=============================================================================
MAXSI_LINK FileSize_t MaxsiGetFileSize(MaxsiHandle FileHandle);

//=============================================================================
//	bool MaxsiCloseFile(MaxsiHandle FileHandle);
//
//	Closes the handle and returns true, or it does something unpleasant and
//	perhaps it then decides on returning false if it hasn't crashed.
//=============================================================================
MAXSI_LINK bool MaxsiCloseFile(MaxsiHandle FileHandle);

//=============================================================================
//	int WriteFileSimple(MESTR* FileName, char* Data, size_t DataSize);
//
//	Opens FileName for writing, writes DataSize bytes starting at Data, and
//	closes FileName and returns 0 if successful.
//=============================================================================
MAXSI_LINK int WriteFileSimple(MESTR* FileName, BYTE* Data, size_t DataSize);

//=============================================================================
//	int ReadFileSimple(MESTR* FileName, char** Data, size_t* DataSize);
//
//	Opens FileName for reading, reads all the contents, closes FileName, sets
//	the pointer pointed to by Data to the contents and sets the size_t pointed
//	to by DataSize to the size of the data buffer read, and returns 0 if
//	successful. You must delete[] this buffer manually when you are done with
//	it. If the file is of zero length then this function returns 0 with Data =
//	NULL and DataSize set to zero.
//=============================================================================
MAXSI_LINK MaxsiError ReadFileSimple(MESTR* FileName, BYTE** Data, size_t* DataSize);
MAXSI_LINK MaxsiError ReadFileSimpleNullTerminated(MESTR* FileName, BYTE** Data, size_t* DataSize);

//=============================================================================
//	int ReadAsciiTextFileSimple(MESTR* FileName, char** Data, size_t* DataSize);
//
//	Opens FileName for reading, reads all the contents, closes FileName, sets
//	the pointer pointed to by Data to the contents and sets the size_t pointed
//	to by DataSize to the size of the data buffer read, and returns 0 if
//	successful. You must delete[] this buffer manually when you are done with
//	it. If the file is of zero length then this function returns 0 with Data =
//	NULL and DataSize set to zero. The returned buffer is safely
//	null-terminated after DataSize characters on success, unlike
//	ReadFileSimple, which cannot be used to	safely read strings with unless you
//	plan on null-terminating it yourself.
//=============================================================================
MAXSI_LINK MaxsiError ReadTextFile(MESTR* Path, MESTR** Data, size_t* DataLength, StringType_t Encoding = MAXSI_STR_DETECT);

//=============================================================================
//	bool CreateFolderRecursively(MESTR* Directory);
//
//	Creates the folder given by Directory and returns true if successfully
//	created and returns false on error. Creates all the folders beneath the
//	requested folder, if they don't exist.
//=============================================================================
MAXSI_LINK bool CreateFolderRecursively(MESTR* Directory);

//=============================================================================
//	bool CompareFileNames(MESTR* File1, MESTR* File2);
//
//	Compares the two file names and returns true if they are identical and
//	point to the same resource.
//=============================================================================
MAXSI_LINK bool CompareFileNames(MESTR* File1, MESTR* File2);

//=============================================================================
//	MESTR* RemoveFilePathExt(MESTR* FilePath);
//
//	Removes the file extension, if any, from FilePath and returns a new string
//	containing the extensionless result.
//=============================================================================
MAXSI_LINK MESTR* RemoveFilePathExt(MESTR* FilePath);

//=============================================================================
//	FileSize_t GetFileSize64(HANDLE FileHandle);
//
//	Finds the filesize of the given handle and returns it as a 64-bit integer.
//=============================================================================
MAXSI_LINK FileSize_t GetFileSize64(MaxsiHandle FileHandle);

//=============================================================================
//	FileSize_t GetFilePathSize64(MESTR* FilePath);
//
//	Finds the filesize of the give file and returns it as a 64-bit integer.
//	NOTE: This function returns FILESIZE_MAX on failure, but FILESIZE_MAX could
//	also be a valid filesize of a very, very big file. This function should not
//	be used unless you validate its result and that the file actually exists
//	and that this process has permission to read its filesize.
//=============================================================================
MAXSI_LINK FileSize_t GetFilePathSize64(MESTR* FilePath);

MAXSI_LINK MESTR* PrintFileSizeShort(FileSize_t Input);

EndMaxsiNamespace

#endif
